//PA2-3
#include "FLOAT.h"
#include <stdint.h>
#include <string.h>

// helper: 64-bit multiply then shift
static inline FLOAT mul_q16(int32_t a, int32_t b) {
    int64_t t = (int64_t)a * (int64_t)b;
    return (FLOAT)(t >> 16);
}

// helper: (a << 16) / b
static inline FLOAT div_q16(int32_t a, int32_t b) {
    nemu_assert(b != 0);
    // compute ((int64)a << 16) / b using x86 64/32 signed division
    uint32_t lo = ((uint32_t)a) << 16;   // low 32 bits of numerator
    int32_t hi = a >> 16;                // high 32 bits (arithmetic) of numerator
    int32_t q, r;
    asm volatile ("idivl %2" : "=a"(q), "=d"(r) : "rm"(b), "a"(lo), "d"(hi));
    return q;
}

//PA2-3
FLOAT f2F(float f) {
    // Convert IEEE-754 float to Q16.16 without using FP instructions
    uint32_t u; memcpy(&u, &f, sizeof(u));
    uint32_t sign = u >> 31;
    uint32_t exp_raw = (u >> 23) & 0xFF;
    uint32_t frac = u & 0x7FFFFF; // 23 bits

    if (exp_raw == 0xFF) {
        // NaN/Inf -> return 0 (undefined for our fixed-point scope)
        return 0;
    }

    int32_t res = 0;
    if (exp_raw == 0) {
        // Denormal: exponent = -126, mantissa = frac (no hidden 1)
        if (frac == 0) return 0;
        int exp = -126; // unbiased
        int shift = exp - 23 + 16; // (mantissa has 23 bits)
        if (shift >= 0) {
            int64_t t = ((int64_t)frac) << shift;
            res = (int32_t)t;
        } else if (shift < -31) {
            res = 0;
        } else {
            res = (int32_t)(frac >> (-shift));
        }
    } else {
        // Normalized: mantissa has hidden leading 1
        uint32_t mant = (1u << 23) | frac; // 24 bits effective
        int exp = ((int)exp_raw - 127);
        int shift = exp - 23 + 16; // bring to Q16.16
        if (shift >= 0) {
            int64_t t = ((int64_t)mant) << shift;
            res = (int32_t)t;
        } else if (shift < -31) {
            res = 0;
        } else {
            res = (int32_t)(mant >> (-shift));
        }
    }

    if (sign) res = -res;
    return (FLOAT)res;
}

//PA2-3
FLOAT F_mul_F(FLOAT a, FLOAT b) {
    return mul_q16(a, b);
}

//PA2-3
FLOAT F_div_F(FLOAT a, FLOAT b) {
    return div_q16(a, b);
}

//PA2-3
FLOAT Fabs(FLOAT a) {
    return (a >= 0) ? a : -a;
}

//PA2-3
FLOAT sqrt(FLOAT a) {
    if (a <= 0) return 0;
    // Newton-Raphson in Q16.16
    FLOAT x = a; // initial guess
    int i;
    for (i = 0; i < 16; i++) {
        // x = (x + a/x)/2
        FLOAT q = F_div_F(a, x);
        x = (x + q) >> 1;
    }
    return x;
}

// not used in PA2-3
FLOAT pow(FLOAT a, FLOAT b) {
    nemu_assert(0);
    return 0;
}

/* End of file */